Chapter 1:
Introduction

In the last lesson, we learned how to create a basic window, add a component like a button or label to it, and interact with a dialog. Now we're going to spice up our GUI repertoire with layouts, which will help us organize multiple components.

As you saw if you tried to put multiple components in a window in your last assignment, they don't play well together. Either you only see the last one you added, or you see one in front of the other, hiding most of the one behind it.

Obviously, that's not how we want to run our windows. In order to organize components in a window, we're going to learn about layouts and layout managers.

But that's not all! We'll also see how to make a text area scroll when there is too much text to fit into it. That feature will come in handy.

Let's get started!

Chapter 2:
Java's Layouts

When it comes to managing components in a window, Java uses layout managers. Java has eight of them, and third-party authors have created others. Each layout manager produces a specific window layout that we can fine-tune to make our windows look the way we want. For this lesson, we'll stick to Java's layout managers. If you want to look at some third-party managers and compare them, they are easy to find by doing an online search for "java layout manager."

We're only going to look at the four most-used Java layout managers in this lesson: BorderLayout, BoxLayout, FlowLayout, and GridLayout. Here's a brief description of each one, along with code that shows how to use them.

BorderLayout

TQA-16 --- BorderLayout divides its window into five parts that have fixed positions relative to each other. Each part can hold one component, and the size of the part controls the component's size. This figure shows the five parts, each with a button added to it:



Java's BorderLayout

We can change the size of each part, but not its position. The north and south areas always keep the same height, but we can change their width. The east and west areas keep the same width, but we can change their height. The center area is the only one whose height and width can both change. For example, if I stretch the above window, here is what it looks like:



Stretched BorderLayout

This may seem like a rigid layout, but it is one of the most commonly used. It's actually more flexible than it might seem, since Java hides any empty outer areas (North, South, East, or West). If there's nothing in them, they do not appear when Java displays the window.

Let's look at the code it takes to generate a window using the BorderLayout manager. Specifically, we'll look at the code to generate the first window we saw above. Right now, we'll focus on the display, and then we'll come back to the listeners and the actions that the buttons perform later.

We can start with the basic shell that we used in the last lesson, with a main() method that creates an object and a start() method that builds the window. Our beginning empty class looks like this:

import java.awt.*; import java.awt.event.*; import javax.swing.*;

public class LayoutBorder { private JFrame frame;

public static void main (String[] args) { LayoutBorder guiLayout = new LayoutBorder(); guiLayout.start(); }

public void start() { frame = new JFrame("Border Layout"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container contentPane = frame.getContentPane();

frame.pack(); frame.setVisible(true); } }

Other than the window title and some class and variable names, we saw all of this code in the last lesson. As it is, this code does not add any components to the window—it only produces an empty window. So let's add some code that will use the BorderLayout manager to put buttons into the five areas of the window.

The first thing we need to do is tell Java that we want to use a layout manager for our window and which manager we want to use. Every GUI Container object contains a setLayout() method to assign a layout manager to the container. Its single argument is the layout manager to assign. We will use contentPane's setLayout() method with a BorderLayout object like this:

contentPane.setLayout(new BorderLayout());

This line has to follow the declaration of contentPane, so we'll add it as the fourth line of our start method.

Once we have the layout manager, we can add buttons to the pane like we did in the previous lesson. When we're dealing with the BorderLayout manager, though, we need to add an additional parameter that tells the layout manager which area to put the button in. To get the window we saw above, we need to add one button to each region, with the region's name as part of the button name, like this:

contentPane.add(new JButton("North Button"), 
                BorderLayout.NORTH);
contentPane.add(new JButton("South Button"),
                BorderLayout.SOUTH);
contentPane.add(new JButton("East Button"),
                BorderLayout.EAST);
contentPane.add(new JButton("West Button"),
                BorderLayout.WEST);
contentPane.add(new JButton("Center Button"),
                BorderLayout.CENTER);
	

These lines follow the setLayout() call and precede the call to the pack() method. That's all there is to it! Our finished start() method looks like this:

public void start() 
{
    frame = new JFrame("Border Layout");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Container contentPane = frame.getContentPane();
    contentPane.setLayout(new BorderLayout());
        
    contentPane.add(new JButton("North Button"),
                    BorderLayout.NORTH);
    contentPane.add(new JButton("South Button"),
                    BorderLayout.SOUTH);
    contentPane.add(new JButton("East Button"),
                    BorderLayout.EAST);
    contentPane.add(new JButton("West Button"),
                    BorderLayout.WEST);
    contentPane.add(new JButton("Center Button"),
                    BorderLayout.CENTER);

frame.pack(); frame.setVisible(true); }

Go ahead and run this program now. You should see the BorderLayout window I showed you above. Please let me know in the Discussion Area if you see something else.

Before we move on to the next layout manager, there are just a few notes on BorderLayout that I'd like to review:

  1. Each BorderLayout region can only contain one component.
  2. If you leave one of the outer areas empty, it won't show up in the window. So you can have a window with North, West, and Center areas but no East or South areas, or any other combination that makes sense in your program.
  3. Even though a region can only hold one component, don't think of this as a limitation. Later in this lesson, we'll see how to make a component (a panel) that can hold other components and even use a different layout manager. Then we can add that external component (with its inner components) to a BorderLayout region, effectively overcoming the one-component limit.

BoxLayout

The next layout manager we'll look at is BoxLayout. BoxLayout allows each component to keep its own size. It stacks components in the order we add them, and we can choose to stack them either vertically or horizontally. We'll use BoxLayout's vertical organization more often than its horizontal layout. Here's an example of how it looks:



Java's BoxLayout

The code for the above BoxLayout window is very similar to the code we used for the BorderLayout window. The only differences are in the lines we introduced in the last section that controlled the BorderLayout. Most of these differences affect the parameters. Let me show you what I mean.

The first line we need to look at is the one that connects the layout manager to the container it will manage (contentPane, in this case). For a BoxLayout manager, that line looks like this:

contentPane.setLayout(new BoxLayout(contentPane,BoxLayout.Y_AXIS));

The call to setLayout still needs a LayoutManager for its parameter. In this example, we created a BoxLayout object for that parameter. The BoxLayout constructor, however, needs some parameters that the BorderLayout constructor did not. The first of those (contentPane in parentheses in the line above) is the container whose layout we want to manage. The second parameter (BoxLayout.Y_AXIS) tells the layout manager whether to stack components vertically or horizontally. In this example, we chose to stack them vertically. If we wanted to lay them out horizontally, we would use a parameter of BoxLayout.X_AXIS instead.

The way we add components to the window in BoxLayout is also very similar to how we did it in BorderLayout. The only difference is that we don't have to tell the layout manager which region to add a component to, since there is only one big region. So the lines to add buttons look like this:

contentPane.add(new JButton("First Button"));
contentPane.add(new JButton("Second Button - a long one"));
contentPane.add(new JButton("Third Button"));
contentPane.add(new JButton("Fourth Button"));
	

If we replace the lines we added to place the buttons in the previous example with the five lines you see here, and change the title of the window, we should get the window I showed you above. Try running this program now. The pane shows the buttons in the order we added them, keeping their original sizes, as you can see from the image above.

You can also see from the two images below that the components in a BoxLayout are not resized or rearranged if the window size changes.



Resized BoxLayout windows

FlowLayout

FlowLayout, like BoxLayout, places all components into the window in the order we add them. It also allows them to keep their original sizes. It is different from BoxLayout in a couple of ways, too. First, it simply flows all components from left to right. Here's a snapshot of a FlowLayout window with the same four buttons we used in the BoxLayout:



Java's FlowLayout

The second difference is that if the window size changes so that it cannot hold all the components from left to right, it wraps them and centers them as you can see here:



Resized FlowLayout

We only need to change one line of code (other than the window title, which I am not counting as a significant change) to change a BoxLayout to a FlowLayout. As you might have guessed, it's the line that calls the pane's setLayout() method to connect the layout manager to the container. Here is the new version:

    
contentPane.setLayout(new FlowLayout());

Like BorderLayout, FlowLayout's constructor does not need any arguments. Try using FlowLayout in your code to see how it works and how it differs from BoxLayout and BorderLayout.

GridLayout

The last of the layouts we'll talk about, GridLayout, puts components into a grid. We can specify the number of rows and columns we want the grid to contain, and all components will have equal size based on the size of the largest component. Here's an example:



Java's GridLayout

We only need to make one significant change to use GridLayout instead of FlowLayout or BoxLayout. It's in the same line of code we changed in the last example—the line that calls the pane's setLayout() method. Here's the call for GridLayout:

contentPane.setLayout(new GridLayout(3,2)); 
	

GridLayout's constructor needs two numeric arguments. The first number tells it the number of rows to put in the grid. The second gives it the number of columns. As we add components to the pane, GridLayout places them from left to right and top to bottom, as the image above shows.

Go ahead and set up a GridLayout in your program to check it out. In fact, if you want to have a little more fun, set up a three-by-three grid of buttons with no text, and make the buttons change their text to X or O when you click them. See if you can figure out how to make the first button you click show an X, the second one show an O, and so on. Then you can play tic-tac-toe on it! If you have any problem creating the above window, be sure to let me know, or check out my solution here.

LayoutGridTicTacToe Solution

Got all that? In the next chapter, we'll tackle panels and see how we can use them to put multiple components anywhere we want to!

Chapter 3:
Panels

Now that you've seen how to use the four most common layout managers in Java, let's see how we can do even more with them by using panels.

TQA-17 --- I said earlier that I'd explain how to use a panel to add multiple components to a layout region that only accepts one. A panel is a component that holds other components. Not only do panels hold multiple other components, but they can each have their own layout managers that can be different from the frame's manager. We build panels from Java's JPanel class. I'll show you how in just a minute. First, let's decide what we want to do in our window.

How about if we create a window that looks like this?



Combined layouts

This window has a text label at the top with instructions, four buttons on the left, and a big text area on the right where text will appear when we click the buttons. To make this happen, we'll need to use two different layouts and a panel. Here's how we'll organize it:



Layout design

For the panel, we'll use a BoxLayout with the components stacked vertically. The panel will be just wide enough for the buttons, and the width of the panel will determine the width of the West region of the outer layout. The North region will take on the height of the text label, and the Center region with its text area will fill the rest of the window.

For our program, let's start with the shell of the program we used for the BorderLayout example, before we added any buttons. It looks like this:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class LayoutCombo { private JFrame frame;

public static void main (String[] args) { LayoutCombo guiLayout = new LayoutCombo(); guiLayout.start(); }

public void start() { frame = new JFrame("Border and Box Layouts"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container contentPane = frame.getContentPane(); contentPane.setLayout(new BorderLayout());

frame.pack(); frame.setVisible(true); } }

Now, what data objects will we need for our window? We know we'll need a text label and four buttons. We already know how to create them. But how do we create a panel and a large text area? We'll use the JPanel class to create a panel, and (appropriately enough) the JTextArea class for the text area. Let's go ahead and declare the variables we'll use at the top of our class:

private JLabel label;
private JButton button1, button2, button3, button4;
private JPanel panel;
private JTextArea textArea; 
	

These declarations go with the frame declaration that is already in the program.

Now that we have the variables we need, let's go down to the start() method and put all these details together. First, right after the line that sets the layout for the window pane, let's add this to create our label and place it at the top of the screen:

label = new JLabel("Click a button to see its text appear below:");
contentPane.add(label, BorderLayout.NORTH);
	

Next, let's create our panel, set its layout, and add our buttons to it. The JPanel constructor does not need any parameters, and we use the same add() method that contentPane uses to add components to a panel. So setting up the panel looks like this:

panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
        
button1 = new JButton("Button 1");
panel.add(button1);
button2 = new JButton("Button 2");
panel.add(button2);
button3 = new JButton("Button 3");
panel.add(button3);
button4 = new JButton("Button 4");
panel.add(button4); 
contentPane.add(panel, BorderLayout.WEST);
	

That code should go right after our label creation lines.

We'll add code for the text area next. The default JTextArea constructor creates an area that will take up whatever space is available in the window. I want to create an area a little bit bigger than that so we have some extra room. Here's the constructor call I'm going to use:

textArea = new JTextArea(10,25);
    

This constructor call will create a text area that is 10 rows tall and 25 columns wide. When setting this up, Java calculates a row height based on the height of the font used in the window. The column width gets set for the width of the letter m. Ten rows and 25 columns give me a text area about the size we want. Once we've created the text area, we can add it to the window:

contentPane.add(textArea, BorderLayout.CENTER);
    

If we run the program now, we'll see the same window we saw in the image at the beginning of the section, minus the text in the text area. We've created all our components, and they look right. The only problem is that they don't do anything. (If you have problems running it at this point and want to compare it to my code, click here.)

LayoutCombo Solution

If we have all the components, what's left to do to make it work? Remember the listeners we attached to buttons in the last lesson? We need to do the same for each of these four buttons. Before we do, though, we need to take care of a couple of related items.

Adding action listeners means we need two other items as well. First, we need to add the code implements ActionListener to the first line of our class declaration, just like we did in the last lesson.

Second, we need to add a method named actionPerformed() to our class because that's what the ActionListener interface requires. That method will contain the action we want to perform when a user clicks a button. In this case, we'll need the method to add text to the text area when each button gets clicked. And that means we need to know which button was clicked.

How do we do that? This is where the ActionEvent that we receive in the actionPerformed() method comes into play. An ActionEvent object has a method named getSource() that will tell us the object that initiated the action. Then we can compare it to our buttons to see which one was clicked.

Here's one way to write that method:

public void actionPerformed(ActionEvent e)
{
    if (e.getSource() == button1)
    textArea.append("Button 1 was clicked.\n");
    else if (e.getSource() == button2)
    textArea.append("Button 2 was clicked.\n");
    else if (e.getSource() == button3)

    textArea.append("Button 3 was clicked.\n");
    else if (e.getSource() == button4)
    textArea.append("Button 4 was clicked.\n");
    else
    textArea.append("Should not get here!\n");}
    

Each if calls the ActionEvent's getSource() method, then compares it to one of our buttons. If they match, we add the appropriate text to our text area using its append() method.

Now we're finally ready to add our listeners. We need to add them to the buttons, just like we did in the last lesson:

button1.addActionListener(this); 
button2.addActionListener(this); 
button3.addActionListener(this); 
button4.addActionListener(this);
	

You can add these lines almost anywhere before the window is displayed. I added the line for each button between the line that created the button and the one that added it to the panel.

The window should now work as advertised. Each click on one of the buttons should add a line of text to the text area naming the button that was just clicked. If your program does not work that way, you can compare it to my latest version here or ask me about it in the Discussion Area.

LayoutCombo implements ActionListener Solution

Chapter 4:
Scrolling

Once you get that last program working, see what happens when you click a button more than 10 times. It looks like the program quits working! It doesn't—it keeps adding text to the text area, but there's no more room to display it, so it doesn't show up. We're going to look at how to fix that by adding a scroll bar to our window so you can scroll up and down to see all the text.

We only need to use one more class in our program to make it scroll. The JScrollPane object provides scroll bars. It will also manage the mouse clicks in the scroll bar and move the text display accordingly. Let's see how to make it work.

TQA-18 --- The first thing we need to do is add one more declaration to our list of window components. We can declare a JScrollPane object like this:

private JScrollPane scrollArea;
    

To make our text scroll, we'll need to do three things. First, instead of adding the textArea to the window pane, we'll pass it to the scroll pane when we call its constructor. That will tell the scroll pane what component will be scrolled. So let's replace the line that added textArea to contentPane with this line:

scrollArea = new JScrollPane(textArea);
    

Second, we need to configure the scrolling area by telling it how we want the scroll bars to appear. There are two scroll bars, a vertical one on the right side that controls up and down motion, and a horizontal one across the bottom that controls left and right scrolling.

We can choose to make each of the bars appear all the time, none of the time (if we know we won't need one of them), or only when they're needed. Since we won't do any right or left scrolling, we'll set the horizontal scroll bar to be invisible. And we'll make the vertical bar invisible until we need it.

To configure the bars, we use two methods from the scroll pane class, setVerticalScrollBarPolicy() and setHorizontalScrollBarPolicy(). The calls look like this:

scrollArea.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollArea.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
	

The constants that control the scroll bar policies can end with ALWAYS, AS_NEEDED, or NEVER, depending on how we want the scroll bars to behave.

Okay, that's two out of three. The last thing we need to do is add the scroll pane to the window instead of adding the text area. We already know how to use contentPane's add() method to do that:

contentPane.add(scrollArea, BorderLayout.CENTER);
    

That's it! We're done! If you run the program now, and click enough buttons, your window should look something like this:



Scrolling text area

Your scroll bar should work exactly like any other, moving the text up and down so you can look at all of it. If you can't get it to work, let me know, or take a look at my finished program here.

LayoutCombo implements ActionListener with Scrolling Solution!

Chapter 5:
Summary

That wraps things up for today. We covered four layout managers—BorderLayout, BoxLayout, FlowLayout, and GridLayout—as well as how to use them. We also worked with some new components, a panel and a scroll pane.

After you've digested all that, try it out in the assignment for this lesson, and let me know if you have any problems with it.

Next time, we'll take things a step further with our GUI. We're going to look at how to add and use one of the most common GUI elements ever: the menu bar and its subcomponents, menus and menu items.

I'll see you then.


Lesson 6 FAQs

Q: Is it possible to create a window and specify its component locations and sizes without using a layout manager?

A: Java's layout managers are designed to simplify the process of laying out windows without regard for the platform the program is running on. Placing multiple components into a window without using a layout manager is possible—layout managers make it much easier to control the appearance of components at different screen sizes.

Lesson 6 Assignment


Your assignment for this lesson is to write a GUI program that will display a window with buttons, a label, and a text area like you see in the figure below. You do not have to lay them out the same way I did, since you may like other layouts better. But the functions should be the same.



Assignment 6 Window

The two upper buttons on the left side set the text area to wrap text (or not) using the JTextArea's setLineWrap() method.

The lower-left button clears any text that has been typed into the text area in the center.

The buttons on the right set the text area's scrolling properties to scroll vertically (only), horizontally (only), both directions, or not at all. The methods to use are the same ones we used in the lesson, but with different arguments.

My solution is here, but please wait to look at it until you have tried the assignment on your own:

Assignment 6 solution

Even if you did not look at my solution to do the assignment, please take a quick look at it anyway, since I handled my event actions in a slightly different way this time. Instead of putting all the actions into the if . . . else logic, I called separate helper methods for each event. We'll see this structure again in the future, so I just wanted to call your attention to it here.

Let me know if you have any questions.

Lesson 6 Quiz Answers

1. How does Java's BoxLayout work?
It stacks components either horizontally or vertically in the window, keeping their original sizes.

2. What information does BorderLayout need to successfully add a component?
The layout region in which to add the component.

3. How does FlowLayout organize GUI components?
Horizontally, from left to right in the window, wrapping them if they do not fit.

4. What information does the GridLayout constructor need in order to organize its components properly?
The number of rows and columns to use.

5. How does a panel's internal layout manager relate to the larger window's layout manager?
There is no relationship between them; they are completely independent.


Lesson 6 Supplementary Materials